;写一个 PrintString 方法，将字符串打印出来

NULL equ 0x00
SETCHAR equ 0x07
VIDEOMEM equ 0xb800
STRINGLEN equ 0xffff
;声明代码段；align=16 按照 16 字节对齐；vstart=0x7c00 代码中的地址在编译时都加上 0x7c00
section code align=16 vstart=0x7c00
    mov si, SayHello
    xor di, di
    call PrintString
    mov si, SayByeBye
    call PrintString
    jmp End

;声明 PrintString 函数
;用 [ds:si] 指向要显示的字符串的地址
;用 [es:di] 指向显存中的地址
PrintString:
    .setup:             ;设置 setup 标号，紧接着的代码是做一些初始化的工作
    mov ax, VIDEOMEM
    mov es, ax          ;将 ds 设置为显示字符的起始位置，用 si 作为偏移地址

    mov bh, SETCHAR     ;显示字符的属性都为白底黑字
    mov cx, STRINGLEN   ;字符串长度最长为 0xffff，所以最多循环那么多次

    .printchar:         ;设置 printchar 标号，表示紧接着的代码是打印字符
    mov bl, [ds:si]     ;将要显示的字符存到 bl 中
    inc si              ;将 di + 1，指向下一个待显示的字符
    mov [es:di], bl     ;将 bl 中存储的字符设置到显存指定地址中
    inc di              ;为了防止覆盖，需要将显存地址 + 1
    mov [es:di], bh     ;设置该字符的属性，即白底黑字
    inc di
    or bl, NULL         ;可以通过标志寄存器判断当前是不是读到了字符串的结尾
    jz .return          ;如果已经读到了字符串的结尾，则跳转到退出的标号处
    loop .printchar     ;继续打印下一个字符

    .return:
    ret

SayHello db 'Hi there,I am Coding Master!'
         db 0x00
SayByeBye db 'I think you can handle it,bye!'
          db 0x00

End: jmp End
times 510-($-$$) db 0
                 db 0x55, 0xaa